﻿Imports Org.BouncyCastle.X509
Imports Org.BouncyCastle.Asn1.X509
Imports Org.BouncyCastle.Math
Imports Org.BouncyCastle.X509.Extension
Imports Org.BouncyCastle.Security
Imports SharedTools
Imports System.Xml
Imports Org.BouncyCastle.Crypto.Parameters
Imports Org.BouncyCastle.Asn1.Ocsp
Imports Org.BouncyCastle.Ocsp
Imports Org.BouncyCastle.Asn1
Imports System.Threading

Public Class CRLManager


    Public Shared Sub RefreshCRL(sender As Object, e As EventArgs)
        'If DirectCast(, Control).Dispatcher.Thread IsNot Thread.CurrentThread Then
        '    DirectCast(sender, Control).Dispatcher.BeginInvoke(New RefreshCRLDelegate(AddressOf RefreshCRL), {sender, e})
        '    Return
        'End If

        If Repository.CRL IsNot Nothing AndAlso Repository.CRL.NextUpdate.Value < DateTime.Now Then
            UpdateCRL()
        End If
    End Sub
    Private Delegate Sub RefreshCRLDelegate(sender As Object, e As Timers.ElapsedEventArgs)

    Public Shared Sub CreateCRL()

        Dim crlGen As New X509V2CrlGenerator
        Dim now = DateTime.Now
        Dim caCrlCert = Repository.Data.MyCertificate

        crlGen.SetIssuerDN(Repository.Data.MyCertificate.SubjectDN)

        crlGen.SetThisUpdate(now)
        crlGen.SetNextUpdate(now.AddSeconds(My.Settings.CRLLife))


        crlGen.SetSignatureAlgorithm("SHA512withRSA")

        crlGen.AddExtension(X509Extensions.AuthorityKeyIdentifier, False, New AuthorityKeyIdentifierStructure(caCrlCert))
        'crlGen.AddExtension(X509Extensions.CrlNumber, False, New CrlNumber(CrlNumber))

        Dim crl = crlGen.Generate(Repository.Data.BouncyCastlePrivateKey, New SecureRandom())
        Repository.CRL = crl
    End Sub

    Public Shared Sub UpdateCRL(Optional serialToRevoke As BigInteger = Nothing)
        If Repository.CRL Is Nothing Then
            CreateCRL()
        End If


        Dim crlGen As New X509V2CrlGenerator
        Dim now = DateTime.Now
        Dim caCrlCert = Repository.Data.MyCertificate

        crlGen.SetIssuerDN(Repository.Data.MyCertificate.SubjectDN)

        crlGen.SetThisUpdate(now)
        crlGen.SetNextUpdate(now.AddSeconds(My.Settings.CRLLife))

        crlGen.SetSignatureAlgorithm("SHA512withRSA")

        crlGen.AddCrl(Repository.CRL)

        If serialToRevoke IsNot Nothing Then
            crlGen.AddCrlEntry(serialToRevoke, DateTime.Now, CrlReason.Unspecified)
        End If

        crlGen.AddExtension(X509Extensions.AuthorityKeyIdentifier, False, New AuthorityKeyIdentifierStructure(caCrlCert))
        'crlGen.AddExtension(X509Extensions.CrlNumber, False, New CrlNumber(CrlNumber))

        Dim crl = crlGen.Generate(Repository.Data.BouncyCastlePrivateKey, New SecureRandom())
        Repository.CRL = crl
    End Sub

    Public Shared Function DownloadCRL(r As String) As String
        If Repository.CRL Is Nothing Then
            CreateCRL()
        End If

        Dim crl = Repository.CRL
        Dim bytes = crl.GetEncoded
        Dim xmlToSign As String = String.Empty

        Dim username = XmlTool.GetValue("Username", r)
        Dim cert = CertificatesManager.GetCurrentCertificateByUsername(username, CertificateType.Signature)
        If cert IsNot Nothing Then
            If XmlTool.CheckSignature(r, DirectCast(cert.Certificate.GetPublicKey, RsaKeyParameters)) Then
                xmlToSign = XmlTool.GenerateXML("DownloadCRLResponse", "CRL", Convert.ToBase64String(bytes))
                xmlToSign = XmlTool.AddAttribute(xmlToSign, "DownloadCRLResponse", "Response", GenericQueryResponse.Ok.ToString)
            Else
                xmlToSign = XmlTool.GenerateXML("DownloadCRLResponse")
                xmlToSign = XmlTool.AddAttribute(xmlToSign, "DownloadCRLResponse", "Response", GenericQueryResponse.SignatureException.ToString)
            End If
        Else
            xmlToSign = XmlTool.GenerateXML("DownloadCRLResponse")
            xmlToSign = XmlTool.AddAttribute(xmlToSign, "DownloadCRLResponse", "Response", GenericQueryResponse.ClientUnknownException.ToString)
        End If

        Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, xmlToSign)

    End Function

    Public Shared Function RevokeCertificate(r As String) As String
        Dim x As New XmlDocument
        x.LoadXml(r)
        Dim resp = String.Empty
        Dim username = x.GetElementsByTagName("Username")(0).InnerText
        Dim id = CInt(x.GetElementsByTagName("ID")(0).InnerText)

        Dim userCertItem = CertificatesManager.GetCertificateByID(id)
        If userCertItem Is Nothing Then
            resp = XmlTool.GenerateXML("RevocationRequestResponse", id)
            resp = XmlTool.AddAttribute(resp, "RevocationRequestResponse", "Response", GenericQueryResponse.ClientUnknownException.ToString)
        Else

            Dim pubKey As RsaKeyParameters = DirectCast(CertificatesManager.GetCurrentCertificateByUsername(username, CertificateType.Signature).Certificate.GetPublicKey, RsaKeyParameters)
            If XmlTool.CheckSignature(r, pubKey) Then
                'when we revoke a signature certificate, the cryptography one gets revoked as well
                If userCertItem.CertificateType = CertificateType.Signature Then
                    Dim userCryptoCert = CertificatesManager.GetCurrentCertificateByUsername(username, CertificateType.Cryptography)
                    If userCryptoCert IsNot Nothing Then
                        CRLManager.UpdateCRL(BigInteger.ValueOf(userCryptoCert.ID))
                        userCryptoCert.Refresh()
                    End If
                End If

                CRLManager.UpdateCRL(BigInteger.ValueOf(userCertItem.ID))
                userCertItem.Refresh()
                PersistenceManager.SaveCertificates()
                PersistenceManager.SaveCRL()

                resp = XmlTool.GenerateXML("RevocationRequestResponse", id)
                resp = XmlTool.AddAttribute(resp, "RevocationRequestResponse", "Response", GenericQueryResponse.Ok.ToString)
            Else
                resp = XmlTool.GenerateXML("RevocationRequestResponse", id)
                resp = XmlTool.AddAttribute(resp, "RevocationRequestResponse", "Response", GenericQueryResponse.SignatureException.ToString)

            End If
        End If
        Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
    End Function

    Public Shared Function OCSPquery(r As String) As String
        Dim username = XmlTool.GetValue("Username", r)
        Dim id = CInt(XmlTool.GetValue("ID", r))
        Dim resp = String.Empty

        Dim userCertItem = CertificatesManager.GetCertificateByID(id)
        If userCertItem Is Nothing Then
            resp = XmlTool.GenerateXML("OCSPQueryResponse", id)
            resp = XmlTool.AddAttribute(resp, "OCSPQueryResponse", "Response", OCSPQueryResponse.ClientUnknownException.ToString)
        Else
            Dim pubKey = DirectCast(userCertItem.Certificate.GetPublicKey, RsaKeyParameters)


            Dim signingKey As RsaKeyParameters
            Dim signingCert = CertificatesManager.GetCurrentCertificateByUsername(username, CertificateType.Signature)
            If signingCert Is Nothing Then
                resp = XmlTool.GenerateXML("OCSPQueryResponse", id)
                resp = XmlTool.AddAttribute(resp, "OCSPQueryResponse", "Response", OCSPQueryResponse.ClientUnknownException.ToString)
            Else
                signingKey = DirectCast(signingCert.Certificate.GetPublicKey, RsaKeyParameters)

                If XmlTool.CheckSignature(r, signingKey) Then
                    Dim reqBytes = Convert.FromBase64String(XmlTool.GetValue("OCSPRequest", r))
                    Dim req As New OcspReq(reqBytes)
                    Dim certToCheck = req.GetRequestList(0).GetCertID

                    If Repository.CRL Is Nothing Then
                        CRLManager.CreateCRL()
                    End If

                    Dim crlentry As X509CrlEntry = Repository.CRL.GetRevokedCertificate(certToCheck.SerialNumber)
                    Dim basicRespGen As New BasicOcspRespGenerator(Repository.Data.BouncyCastlePublicKey)
                    Dim ocspResp As OCSPQueryResponse
                    If crlentry Is Nothing Then
                        'still valid
                        basicRespGen.AddResponse(certToCheck, Org.BouncyCastle.Ocsp.CertificateStatus.Good)
                        ocspResp = OCSPQueryResponse.Valid
                    Else
                        'revoked
                        Dim dt As New DerGeneralizedTime(crlentry.RevocationDate)
                        Dim rinfo As New RevokedInfo(dt, New CrlReason(CrlReason.CessationOfOperation))
                        Dim rstatus As New RevokedStatus(rinfo)
                        basicRespGen.AddResponse(certToCheck, rstatus)
                        ocspResp = OCSPQueryResponse.Revoked
                    End If
                    Dim response As BasicOcspResp = basicRespGen.Generate("SHA512withRSA", Repository.Data.BouncyCastlePrivateKey, New X509Certificate() {Repository.Data.MyCertificate}, DateTime.Now)
                    Dim responseTbsBytes As Byte() = response.GetTbsResponseData
                    Dim responseAlgorithmBytes = response.SignatureAlgOid
                    Dim responseSignatureBytes = response.GetSignature
                    Dim responseCertificate = response.GetCerts(0).GetEncoded
                    Dim ident As New AlgorithmIdentifier(response.SignatureAlgOid)


                    resp = XmlTool.GenerateXML("OCSPQueryResponse", "OCSPResponse", Convert.ToBase64String(response.GetEncoded), id)

                    resp = XmlTool.AddAttribute(resp, "OCSPQueryResponse", "Response", GenericQueryResponse.Ok.ToString)
                    resp = XmlTool.AddAttribute(resp, "OCSPQueryResponse", "OCSPResponse", ocspResp.ToString)
                    If crlentry IsNot Nothing Then
                        resp = XmlTool.AddAttribute(resp, "OCSPQueryResponse", "RevocationDate", crlentry.RevocationDate.ToString)
                    End If
                Else
                    resp = XmlTool.GenerateXML("OCSPQueryResponse", id)
                    resp = XmlTool.AddAttribute(resp, "OCSPQueryResponse", "Response", GenericQueryResponse.SignatureException.ToString)
                End If
            End If
        End If


        Return XmlTool.SignXml(Repository.Data.BouncyCastlePrivateKey, resp)
    End Function
End Class
